#include "Level.h"
#include "Files.h"
#include <regex>

using namespace std;
list<SceneObject*> World::sceneObjects;
list<UsedMaterial> World::sceneTextures;
list<Material*> World::sceneMaterials;

/*list<SceneObject*>* GetSceneObjects(){
	return &sceneObjects;
}

list<UsedMaterial>* GetSceneMaterials(){
	return &sceneMaterials;
}*/

UsedMaterial::UsedMaterial(Texture* nTex, string name){
	tex = nTex;
	texName = name;
}

void World::BuildLevel(){
	
	// define shaders here
	ShaderProgram* unlit = new ShaderProgram("unlit.vert", "unlit.frag");
	ShaderProgram* diffuse = new ShaderProgram("unlit.vert", "unlit.frag");
	ShaderProgram* selfIllum = new ShaderProgram("unlit.vert", "unlit.frag");
	ShaderProgram* bumpDiffuse = new ShaderProgram("unlit.vert", "unlit.frag");
	ShaderProgram* bumpDiffuseSpecular = new ShaderProgram("unlit.vert", "unlit.frag");
	ShaderProgram* bumpDiffuseTesselated = new ShaderProgram("unlit.vert", "unlit.frag");

	FileBuffer defaultModelFile("models/cube.obj");
	FileBuffer defaultMatFile("materials/default.tga");
	
	FileBuffer defaultMatProp("materials/default.prop");
	FileBuffer defaultModelProp("models/cube.prop");

	if (!defaultMatFile.Exists() || !defaultMatProp.Exists()){
		Engine::ThrowError("Missing default material");
	}

	if (!defaultModelFile.Exists() || !defaultModelProp.Exists()){
		Engine::ThrowError("Missing default model");
	}

	sceneTextures.push_back(UsedMaterial(new Texture("materials/default.tga"), "materials/default.tga"));

	// actually check the prop file locations first so it can load default models/materials instead of breaking the program
	// also check any other essential properties


	//!!!!!!!!!!!!!!!!!!
	// dont use code below, re write it















	//----------------------------------------------------------------------------------------------------
	// test material

	ShaderProgram* nProgram = NULL;
	string propFile = "materials/default.prop";
	FileBuffer testMaterial(propFile);
	string shader;
	list<string> matProps = testMaterial.ReadLines();
	for (list<string>::iterator it = matProps.begin(); it != matProps.end(); ++it){
		regex re("(shader)");
		smatch match;
		if (regex_search(*it, match, re)){
			regex reComment("\/\/(\s*.+$)");
			if (!regex_search(*it, match, reComment)){
				re = regex("\\=\\s*(.*)");
				if (regex_search(*it, match, re)){
					shader = match[1].str();
					shader.erase(std::remove_if(shader.begin(), shader.end(), isspace), shader.end());
					break;
				}
				else{
					Engine::LogError(propFile+" defines an unknown shader. Switched to unlit by default.", Engine::errorType::TYPE_WARNING);
					shader = "unlit";
				}
			}
		}

	}

	if (shader == "unlit"){
		nProgram = unlit;
	}
	else if (shader == "diffuse"){
		nProgram = diffuse;
	}
	else if (shader == "selfIllum"){
		nProgram = selfIllum;
	}
	else if (shader == "bumpDiffuse"){
		nProgram = bumpDiffuse;
	}
	else if (shader == "bumpDiffuseSpecular"){
		nProgram = bumpDiffuseSpecular;
	}
	else if (shader == "bumpDiffuseTesselated"){
		nProgram = bumpDiffuseTesselated;
	}

	Material testMat(nProgram, matProps);
	Material testMat2(nProgram, matProps);
	Material testMat3(nProgram, matProps);

	// test model
	FileBuffer testModel("models/teapot.prop");
	list<string> modelProps = testMaterial.ReadLines();
	
	//SceneObject(int id, bool stat, glm::vec3 pos, glm::vec3 rot, glm::vec3 sca, Material mat, string matName, list<std::string> modelProps, string mdlName){
	string modelName = "models/teapot";
	string MatName = "materials/default";
	SceneObject* testMDL = new SceneObject(0, 1, glm::vec3(0, 0, 0), glm::vec3(0, 0, 0), glm::vec3(1, 1, 1), testMat, MatName, testModel.ReadLines(), modelName);
	testMDL->AssignMaterial(testMat);
	sceneObjects.push_back(testMDL);


	//---------------------------------------------------------------------------

	// read level data

	// each texture file thats read, check if of with the same name has been added to the sceneMaterials vector, if it has, use the pointer in the vector
	// to save resources

	// for each object in the level file create an object like this in a loop:
	// use object pointers like this so scene objects stay in memory
	// then add them to the sceneobjects pointer list


	//SceneObject* obj = new SceneObject();

	// find some way to delete these later so we dont get memory leaks
	// create a list of pointers for each of these objects so we can delete out of memory

	// example:
	// read material properties before sending it to the constructor so we send the right shaderprogram pointer
	// if the shader specified in the material properies is invalid, send it the material properties of the default material

	//!!!!!!
	// how do we stop multiple copies of the same texture in memory?

	// SceneObject(index, 0, glm::vec3(xpos, ypos, zpos), glm::vec3(xrot, yrot, zrot), glm::vec3(xscale, yscale, zscale), materialProps, modelProps, &ShaderProgram);
	string numTextures = to_string(sceneTextures.size());
	string numMaterials = to_string(sceneMaterials.size());
	int numMeshes = 0;

	for (list<SceneObject*>::iterator it = sceneObjects.begin(); it != sceneObjects.end(); ++it){
		numMeshes += (*it)->GetPrimCount();
	}
	string numModels = to_string(numMeshes);

	Engine::LogError("Loaded " + numTextures + " textures, " + numMaterials + " materials, " + numModels + " models", Engine::errorType::TYPE_INFO);
}

void World::UnloadLevel(){

}

void World::UnloadMaterials(){

}

void World::SetLevel(string fName){

}